1 module hip.util.format;
2 import hip.util.string:count;
3 import hip.util.conv:to;
4 
5 string format(string fmt, Args...)(Args a)
6 {
7     string ret = "";
8     int currArg = 0;
9 
10     string[] converted;
11     static foreach(arg; a)
12         converted~= to!string(arg);
13 
14     static assert(fmt.count("%s") == a.length, "Format specifiers count must match arguments count");
15 
16     for(int i = 0; i < fmt.length; i++)
17     {
18         if(i + 1 < fmt.length && fmt[i] == '%' && fmt[i+1] == 's')
19         {
20             i++;
21             ret~= converted[currArg++];
22         }
23         else
24             ret~= fmt[i];
25     }
26     return ret;
27 }
28 
29 string formatFromType(T)(T value)
30 {
31     string ret;
32     mixin(()
33     {
34         string base = T.init.toString;
35         ptrdiff_t start = -1, lastCapture;
36         string ret;
37         foreach(i, c; base)
38         {
39             import hip.util.string;
40             if(start != -1 && (isWhitespace(c) || !(isAlpha(c) || isNumeric(c))))
41             {
42                 ret~= "ret~= \""~base[lastCapture..start]~"\";";
43                 ret~= "ret~= value."~base[start+1..i]~".to!string;";
44                 lastCapture = i;
45                 start = -1;
46             }
47             else if(c == '$')
48                 start = i;
49         }
50         ret~= "ret~= \""~base[lastCapture..$]~"\";";
51         return ret;
52     }());
53     return ret;
54 }
55 
56 void formatFromType(T, Sink)(ref Sink s, T value)
57 {
58     import hip.util.to_string_range;
59     mixin(()
60     {
61         string base = T.init.toString;
62         ptrdiff_t start = -1, lastCapture;
63         string ret;
64         foreach(i, c; base)
65         {
66             import hip.util.string;
67             if(start != -1 && (isWhitespace(c) || !(isAlpha(c) || isNumeric(c))))
68             {
69                 ret~= "put(s, \""~base[lastCapture..start]~"\");";
70                 ret~= "toStringRange(s, value."~base[start+1..i]~");";
71                 lastCapture = i;
72                 start = -1;
73             }
74             else if(c == '$')
75                 start = i;
76         }
77         ret~= "put(s, \""~base[lastCapture..$]~"\");";
78         return ret;
79     }());
80 }
81 
82 /** 
83 *   Unsafe function. It requires the programmer to use it correctly as the buffer size is preallocated.
84 */
85 string fastUnsafeCTFEFormat(in string str, string[] replaceWith...) @trusted
86 {
87     assert(__ctfe, "Can't be called on runtime. To force a CTFE usage, call as `enum variable = \"%s a = 500;\".fastUnsafeCTFEFormat(\"int\");`");
88     char[] buffer;
89     ptrdiff_t replaceSize;
90     foreach(r; replaceWith)
91         replaceSize+= cast(ptrdiff_t)r.length - cast(ptrdiff_t)"%s".length;
92     buffer = new char[str.length + replaceSize];
93     size_t leftBound = 0;
94     size_t leftBoundInput = 0;
95     size_t rightBound = 0;
96     size_t currArg = 0;
97     for(int i = 0; i < str.length; i++)
98     {
99         if(i + 1 < str.length && str[i] == '%' && str[i+1] == 's')
100         {
101             buffer[leftBound..rightBound] = str[leftBoundInput..i];
102             i++;
103             leftBoundInput = i+1;
104             string r = replaceWith[currArg++];
105             buffer[rightBound..rightBound+r.length] = r[];
106             leftBound = rightBound = rightBound+r.length;
107             if(currArg == replaceWith.length)
108                 break;
109         }
110         else
111             rightBound++;
112     }
113     buffer[leftBound..$] = str[leftBoundInput..$];
114     return cast(string)buffer;
115 }